#include "icache/seq_reader.h"
#include "icache/coordinator.h"
#include "icache/subscribe.h"
#include "mswin/file_dir.h"
#include "imglib/io.h"
#include "xml/stream.h"

namespace icache {

// img_read_order_queue

bool img_read_order_queue::Push(img_read_order const & o) 
{
	DequeType::reverse_iterator i;
	for (i = m_Queue.rbegin(); i != m_Queue.rend(); ++i) {
		if (i->frame_num == o.frame_num) {
			if (i->priority < o.priority)
				i->priority = o.priority;
			return true;
		}
	}

	for (i = m_Queue.rbegin(); i != m_Queue.rend(); ++i) {
		if (i->priority >= o.priority) {
			m_Queue.insert(i.base(), o);
			return true;
		}
	}

	m_Queue.push_back(o);

	return true;
}

// seq_frame_request

seq_frame_request::seq_frame_request(seq_reader & r, int n, nearby may_be_requested_later, int p) : 
	still_image_id(r.full_framepath(n)), reader(r), frame_num(n), desired(may_be_requested_later), priority(p) 
{ }

void seq_frame_request::load(byte_image & im) const
	{ assert(!"the method is prohibited to use"); }

void seq_frame_request::request() const
	{ reader.request_and_wait(frame_num, desired, priority); }

//image sequence reader

seq_reader::seq_reader(const string & filename, icache::coordinator * c) : 
	_subscriptions(new subscriptions),
	_coordinator(c)
{
	if (!filename.empty())
		open_by_example(filename);
}

seq_reader::seq_reader(const file_sequence & seq, icache::coordinator * c) : 
	_subscriptions(new subscriptions),
	_coordinator(c)
{
	open(seq);
}

seq_reader::~seq_reader()
{
	//thread should terminate before destruction will take place
	close();
	Terminate();
	resume();
	wait_finish();
}

void seq_reader::open(const file_sequence & seq)
{
	assert(!seq.empty());
	close();
	auto_lock lock(*this);

	img::file_info info;
	if (!img::our_gdiplus_multireader().ping(seq.full_framepath(0), &info))
		throw std::runtime_error(string("could not read file or unknown image format: ") + seq.full_framepath(0));
	_dim = info.dim;
	_alpha = info.bit_count == 32;

	if (&_seq != &seq)
		_seq = seq;

	resume();
}

void seq_reader::open_by_example(const string & filename)
{
	file_sequence seq;
	seq.create_by_example(filename);
	open(seq);
}

void seq_reader::open_by_filter(const string & filter)
{
	file_sequence seq;
	seq.create_by_filter(filter);
	open(seq);
}

void seq_reader::close()
{
	if (!is_open())
		return;
	WaitIdle();
	suspend();
	auto_lock lock(*this);
	_seq.clear();
}

void seq_reader::request(const OrderType & o) {
	if (o.priority > GetThreadPriority(handle())) 
		SetThreadPriority(handle(), o.priority);
	superclass::Place(o);
}

void seq_reader::request(int frame, nearby may_be_requested_later, int priority)
{
	request(img_read_order(frame, priority));
	if (frame != 0 && (may_be_requested_later & backward))
		request(img_read_order(frame-1, priority-1));
	if (frame != duration()-1 && (may_be_requested_later & forward))
		request(img_read_order(frame+1, priority-1));
}

void seq_reader::wait_for_frame(int frame)
{
	subscriber s(*_subscriptions, frame);
	assert(_coordinator);
	if (_coordinator->touch(seq_reader::frame_id(frame)))
		return;
	s.wait();
}

void seq_reader::request_and_wait(int frame, nearby may_be_requested_later, int priority)
{
	request(frame, may_be_requested_later, priority);
	wait_for_frame(frame);
}

void seq_reader::Execute(const OrderType & o)
{
	mswin::thread::set_debug_name("icache::seq_reader");
	//high priority orders were already processed?
	if (o.priority < GetThreadPriority(handle())) 
		SetThreadPriority(handle(), o.priority);

	assert(_coordinator);
	item_id_sptr id(seq_reader::frame_id(o.frame_num));
	if (!_coordinator->touch(id))
		_coordinator->get(id);
	_subscriptions->event(o.frame_num);
}

string seq_reader::description() const
{
	return string("image sequence '") + _seq.filter() + "'";
}

int seq_reader::size() const
{
	return duration();
}

item_id_sptr seq_reader::frame_id(int frame)
{
	return still_image_id::create(full_framepath(frame));
}

cached_image_sptr seq_reader::get(int frame, nearby may_be_requested_later, bool wait)
{
	assert(frame >= 0 && frame < duration());
	item_id_sptr request(seq_frame_request::create(*this, frame, may_be_requested_later, 
		GetThreadPriority(GetCurrentThread())));
	if (wait)
		return _coordinator->get(request);
	else {
		cached_image_sptr p = try_get(frame);
		if (!p)
			request->request();
		return p;
	}
}

cached_image_sptr seq_reader::try_get(int frame)
{
	return _coordinator->try_get(still_image_id::create(full_framepath(frame)));
}

icache::cache * seq_reader::cache() const
{
	return _coordinator;
}

int seq_reader::first_frame_num() const
{
	return _seq.first_frame_num();
}

size_extent seq_reader::dim() const 
{
	return _dim;
}

bool seq_reader::alpha_channel() const
{
	return _alpha;
}

fraction seq_reader::fps() const
{
	return fraction(0,0); //invalid fraction
}

string seq_reader::filepath() const
{
	return full_framepath(0);
}

void seq_reader::coordinator(icache::coordinator * c) {
	if (is_open())
		WaitIdle();
	_coordinator = c;
}

xml::stream & seq_reader::serialize(xml::stream & s, string const & reference_path) 
{
	xml::node & r = s.root();
	if (s.out()) {
		assert(is_open());
		r["type"] = "seq";
		r["filter"] = mswin::relative(_seq.filter(), reference_path);
	}
	else {
		if ((string)r["type"] != "seq")
			throw exception("couldn't initialize Image Sequence from XML stream");
		open_by_filter(mswin::absolute(r["filter"], reference_path));
	}
	return s;
}

} //namespace icache
